package info.batcloud.fanli.core.service.impl;

import com.ctospace.archit.common.pagination.Paging;
import info.batcloud.fanli.core.dto.AgentDTO;
import info.batcloud.fanli.core.dto.DistrictAgentDTO;
import info.batcloud.fanli.core.constants.CacheNameConstants;
import info.batcloud.fanli.core.entity.DistrictAgent;
import info.batcloud.fanli.core.entity.Region;
import info.batcloud.fanli.core.entity.User;
import info.batcloud.fanli.core.enums.AgentStatus;
import info.batcloud.fanli.core.exception.BizException;
import info.batcloud.fanli.core.helper.PagingHelper;
import info.batcloud.fanli.core.repository.DistrictAgentRepository;
import info.batcloud.fanli.core.repository.UserRepository;
import info.batcloud.fanli.core.service.DistrictAgentService;
import info.batcloud.fanli.core.service.RegionService;
import info.batcloud.fanli.core.service.UserService;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import javax.inject.Inject;
import javax.persistence.criteria.Expression;
import javax.persistence.criteria.Predicate;
import java.util.Date;
import java.util.List;

import static info.batcloud.fanli.core.enums.AgentStatus.DELETED;

@Service
@CacheConfig(cacheNames = CacheNameConstants.AGENT)
public class DistrictAgentServiceImpl implements DistrictAgentService {

    @Inject
    private DistrictAgentRepository districtAgentRepository;

    @Inject
    private UserRepository userRepository;

    @Inject
    private UserService userService;

    @Inject
    private RegionService regionService;

    @Override
    public Paging<DistrictAgentDTO> search(SearchParam param) {
        Specification<DistrictAgent> specification = (root, query, cb) -> {
            Predicate predicate = cb.conjunction();
            List<Expression<Boolean>> expressions = predicate.getExpressions();
            if (param.getPerpetual() != null) {
                expressions.add(cb.equal(root.get("perpetual"), param.getPerpetual()));
            }

            if (StringUtils.isNotBlank(param.getPhone())) {
                expressions.add(cb.like(root.get("user").get("phone"), param.getPhone()));
            }
            if (param.getStartUpdateTime() != null) {
                expressions.add(cb.greaterThanOrEqualTo(root.get("updateTime"), param.getStartUpdateTime()));
            }
            if (param.getCityId() != null) {
                expressions.add(cb.equal(root.get("cityId"), param.getCityId()));
            }
            if (param.getDistrictId() != null) {
                expressions.add(cb.equal(root.get("districtId"), param.getDistrictId()));
            }
            if (param.getEndUpdateTime() != null) {
                expressions.add(cb.greaterThanOrEqualTo(root.get("updateTime"), param.getEndUpdateTime()));
            }
            if (param.getStatus() != null) {
                expressions.add(cb.equal(root.get("status"), param.getStatus()));
            } else {
                expressions.add(cb.notEqual(root.get("status"), AgentStatus.DELETED));
            }
            return predicate;
        };
        Sort sort = new Sort(Sort.Direction.DESC, "updateTime");
        Pageable pageable = new PageRequest(param.getPage() - 1,
                param.getPageSize(), sort);
        Page<DistrictAgent> page = districtAgentRepository.findAll(specification, pageable);
        return PagingHelper.of(page, item -> toAgent(item), param.getPage(), param.getPageSize());
    }

    @Override
    @Transactional
    @CacheEvict(key = "'IS_' + #param.getUserId() + '_AGENT_DISTRICT'")
    public DistrictAgentDTO addAgent(AgentAddParam param) {
        Assert.notNull(param.getDistrictId(), "district id does not null");
        User user = userRepository.findOne(param.getUserId());
        //如果当前user不是顶级用户，那么需要修改其为顶级用户
        if (user.getSuperUser() != null) {
            //这里需要修改user的层级关系,并且同步修改其所有下线的层级关系
            userService.changeSuperUser(param.getUserId(), null);
        }
        Date now = new Date();
        Date startTime;
        DistrictAgent eAgent = districtAgentRepository.findByDistrictIdAndStatusIsNot(param.getDistrictId(), DELETED);
        if (eAgent != null && !eAgent.getUser().getId().equals(param.getUserId())) {
            throw new BizException("县代已经存在");
        }
        DistrictAgent agent = districtAgentRepository.findByUserIdAndStatusIsNot(param.getUserId(), DELETED);
        if (agent == null) {
            agent = new DistrictAgent();
            agent.setCreateTime(now);
            startTime = now;
        } else {
            if (agent.getStatus() != AgentStatus.VALID) {
                agent.setCreateTime(now);
                startTime = now;
            } else {
                startTime = agent.getExpireTime();
            }

        }
        Region district = regionService.findById(param.getDistrictId());
        Region city = regionService.findById(district.getParentId());
        Region province = regionService.findById(city.getParentId());
        agent.setCityId(city.getId());
        agent.setProvinceId(province.getId());
        agent.setDistrictId(param.getDistrictId());
        agent.setUpdateTime(new Date());
        if (param.getYear() == -1) {
            //如果是-1那么说明是永久代理
            agent.setExpireTime(DateUtils.addYears(startTime, 100));
            agent.setPerpetual(true);
        } else {
            agent.setExpireTime(DateUtils.addYears(startTime, param.getYear()));
        }
        agent.setStatus(AgentStatus.VALID);
        agent.setUser(user);
        districtAgentRepository.save(agent);
        return toAgent(agent);
    }

    private DistrictAgentDTO toAgent(DistrictAgent entity) {
        DistrictAgentDTO agentBO = new DistrictAgentDTO();
        BeanUtils.copyProperties(entity, agentBO);
        AgentDTO.User user = new AgentDTO.User();
        BeanUtils.copyProperties(entity.getUser(), user);
        agentBO.setUser(user);
        Region city = regionService.findById(entity.getCityId());
        Region province = regionService.findById(city.getParentId());
        agentBO.setCity(city.getName());
        agentBO.setProvince(province.getName());
        agentBO.setProvinceId(province.getId());
        agentBO.setDistrict(regionService.findById(entity.getDistrictId()).getName());
        return agentBO;
    }
}
